﻿/**
 * Licensed to Open-Ones Group under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Open-Ones Group licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a
 * copy of the License at:
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on
 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
namespace IPresenter
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq; // support command messageBuffers.Add(Encoding.Unicode.GetString(ilistBuffer[i].Array.Skip...
    using System.Net;
    using System.Net.Sockets;
    using System.Runtime.InteropServices;
    using System.Security;
    using System.Text;
    using System.Threading;
    using System.Windows.Forms;
    using NETWORKLIST;

    /// <summary>
    /// This class provides functions to work with Socket via wifi
    /// </summary>
    public class NetworkSupport
    {
        /// <summary>
        /// Max length of message about length of message
        /// </summary>
        public const int MAX_SIZE_OF_MESSAGE_LENGTH = 4;

        /// <summary>
        /// Total device(PC) is scaned 
        /// </summary>
        private static int TotalEntries = 0;

        /// <summary>
        /// Get List of PC
        /// </summary>
        /// <param name="serverName">Server Name</param>
        /// <param name="level">DwLevel paramater</param>
        /// <param name="buffer">Pointer Buffer</param>
        /// <param name="maxLength">Unknownn paramater</param>
        /// <param name="entriesRead">Total device is read</param>
        /// <param name="totalEntries">Total device</param>
        /// <param name="serverType">Type of server</param>
        /// <param name="domain">Domain name</param>
        /// <param name="resumeHandle">Unknownn paramater</param>
        /// <returns>Unknown result</returns>
        [DllImport("Netapi32", CharSet = CharSet.Auto, SetLastError = true), SuppressUnmanagedCodeSecurityAttribute]
        public static extern int NetServerEnum(string serverName, int level, ref IntPtr buffer, int maxLength, out int entriesRead, out int totalEntries, int serverType, string domain, out int resumeHandle);

        /// <summary>
        /// Delete memory
        /// </summary>
        /// <param name="buffer">Pointer buffer</param>
        /// <returns>Unknown result</returns>
        [DllImport("Netapi32", SetLastError = true), SuppressUnmanagedCodeSecurityAttribute]
        public static extern int NetApiBufferFree(IntPtr buffer);

        /// <summary>
        /// Get network computer
        /// </summary>
        /// <param name="progressBar">Progress bar is used to run while scanning computer</param>
        /// <param name="listView">Contain list of computer</param>
        /// <returns>List networks</returns>
        public static ArrayList GetNetworkComputers(ProgressBar progressBar, ListView listView)
        {
            progressBar.Visible = true;
            listView.Clear();
            Thread.Sleep(100);
            ArrayList networkComputers = new ArrayList();
            const int MAX_PREFERRED_LENGTH = -1;
            int intSvTypeWorkstation = 1;
            int intSvTypeServer = 2;
            IntPtr iBuffer = IntPtr.Zero;
            IntPtr iTmpBuffer = IntPtr.Zero;
            int intEntriesRead = 0;
            int intResHandle = 0;
            int intSizeofINFO = Marshal.SizeOf(typeof(_SERVER_INFO_100));
            try
            {
                int intRet = NetServerEnum(null, 100, ref iBuffer, MAX_PREFERRED_LENGTH, out intEntriesRead, out TotalEntries, intSvTypeWorkstation | intSvTypeServer, null, out intResHandle);
                progressBar.Maximum = TotalEntries;
                progressBar.Value = 0;
                if (intRet == 0)
                {
                    for (int i = 0; i < TotalEntries; i++)
                    {
                        iTmpBuffer = new IntPtr((int)iBuffer +
                                   (i * intSizeofINFO));

                        _SERVER_INFO_100 svrInfo = (_SERVER_INFO_100)Marshal.PtrToStructure(iTmpBuffer, typeof(_SERVER_INFO_100));
                        networkComputers.Add(svrInfo.sv100_name);
                        progressBar.Value = i;
                        ListViewItem lvit = new ListViewItem();
                        lvit.Text = svrInfo.sv100_name;
                        lvit.ImageIndex = 0;
                        lvit.Tag = svrInfo;
                        listView.Items.Add(lvit);
                        Thread.Sleep(50);
                    }
                }
            }
            catch (Exception ex)
            {
                Logs.WriteErrorMessage("Problem with acessing " + "network computers in NetworkBrowser " + "\r\n\r\n\r\n" + ex.Message);
                return null;
            }
            finally
            {
                int intTemp = NetApiBufferFree(iBuffer);
                if (intTemp != 0)
                {
                    Logs.WriteErrorMessage("Get network successfully");
                }
            }

            progressBar.Visible = false;
            Threads.SwitchThread(3, false);
            Threads.ClientGetNetwork.Abort();
            return networkComputers;
        }

        /// <summary>
        /// Send Message to another device
        /// </summary>
        /// <param name="writerDevice">Socket that use to send message</param>
        /// <param name="message">Message which you would like to send</param>
        /// <returns>
        /// true: success
        /// false: fail
        /// </returns>
        public static bool SendMessage(Socket writerDevice, string message)
        {
            try
            {
                if (writerDevice.Connected)
                {
                    Byte[] data = new Byte[100];
                    data = Encoding.Unicode.GetBytes(message);
                    int length = Encoding.Unicode.GetByteCount(message);
                    writerDevice.Send(Encoding.Unicode.GetBytes(length.ToString()));
                    Thread.Sleep(50);
                    writerDevice.Send(data, data.Length, SocketFlags.None);
                    return true;
                }
            }
            catch (Exception ex)
            {
                Logs.WriteErrorMessage(ex.Message);
                return false;
            }

            return false;
        }

        /// <summary>
        /// Filter message after message had been received
        /// </summary>
        /// <param name="message">Message which you would like to filter</param>
        /// <param name="separateCharacter">Char which used to seperate</param>
        /// <returns>Result after filter</returns>
        public static string FilterMessage(string message, char separateCharacter)
        {
            for (int i = 0; i < message.Length; i++)
            {
                if (message[i] == separateCharacter)
                {
                    message = message.Remove(i);
                    i--;
                }
            }

            return message;
        }

        /// <summary>
        /// Analysis list of message after received
        /// </summary>
        /// <param name="messages">List of message</param>
        /// <returns>List of message was analysis</returns>
        public static List<string> AnalysisMessage(List<string> messages)
        {
            List<string> lstResult = new List<string>();
            for (int i = 0; i < messages.Count; i++)
            {
                string[] strResult = messages[i].Split(new char[] { '/' });
                if (i < messages.Count - 1)
                {
                    messages[i + 1] = messages[i + 1].Insert(0, strResult[strResult.Length - 1]);
                }

                for (int j = 0; j < strResult.Length; j++)
                {
                    lstResult.Add(strResult[j]);
                }
            }

            return lstResult;
        }

        /// <summary>
        /// Send message to another device and encoding of message is Unicode.
        /// </summary>
        /// <param name="writerDevice">Socket that use to send message</param>
        /// <param name="messages">List of message</param>
        /// <returns>
        /// true: success
        /// false: fail
        /// </returns>
        public static bool SendMessageUnicode(Socket writerDevice, List<string> messages)
        {
            try
            {
                if (writerDevice.Connected == false)
                {
                    return false;
                }

                IList<ArraySegment<byte>> ilistBuffer = new List<ArraySegment<byte>>();
                int intLength = 0;
                foreach (string strTemp in messages)
                {
                    ilistBuffer.Add(new ArraySegment<byte>(Encoding.Unicode.GetBytes(strTemp + '/')));
                    intLength += Encoding.Unicode.GetByteCount(strTemp);
                }

                writerDevice.Send(Encoding.Unicode.GetBytes(intLength.ToString()));
                Thread.Sleep(50);
                writerDevice.Send(ilistBuffer);
            }
            catch (Exception ex)
            {
                Logs.WriteErrorMessage(ex.Message);
                return false;
            }

            return true;
        }

        /// <summary>
        /// Receive message from another device and encoding of message is Unicode
        /// </summary>
        /// <param name="readerDevice">Socket that use to receive message</param>
        /// <param name="messageBuffers">Contains that messages is received</param>
        /// <param name="length">Length of result</param>
        /// <returns>
        /// true: success
        /// false: fail
        /// </returns>
        public static bool ReceiveMessageUnicode(Socket readerDevice, out List<string> messageBuffers, int length)
        {
            messageBuffers = new List<string>();
            try
            {
                if (readerDevice.Connected == false)
                {
                    return false;
                }

                IList<ArraySegment<byte>> ilistBuffer = new List<ArraySegment<byte>>();
                StringBuilder strBuilderBuffer = new StringBuilder(length);
                for (int i = 0; i < length; i++)
                {
                    strBuilderBuffer.Append('*');
                }

                ilistBuffer.Add(new ArraySegment<byte>(Encoding.Unicode.GetBytes(strBuilderBuffer.ToString())));
                readerDevice.Receive(ilistBuffer);
                int intStart = 0;
                int intEnd = ilistBuffer.Count;
                for (int i = 0; i < ilistBuffer.Count; i++)
                {
                    while (intEnd % 1000 > 0)
                    {
                        messageBuffers.Add(Encoding.Unicode.GetString(ilistBuffer[i].Array.Skip(intStart).Take(intStart + 1000).ToArray()));
                        intEnd -= 1000;
                        intStart += 1000;
                    }

                    messageBuffers.Add(Encoding.Unicode.GetString(ilistBuffer[i].Array.Skip(intStart).Take(intEnd).ToArray()));
                }
            }
            catch (Exception ex)
            {
                Logs.WriteErrorMessage(ex.Message);
                return false;
            }

            return true;
        }

        /// <summary>
        /// Send message to another device and encoding of message is ASCII
        /// </summary>
        /// <param name="writerDevice">Socket that use to send message</param>
        /// <param name="message">Message which you would like to send</param>
        /// <returns>
        /// true: success
        /// false: fail
        /// </returns>
        public static bool SendMessageAscii(Socket writerDevice, string message)
        {
            try
            {
                if (writerDevice.Connected == false)
                {
                    return false;
                }

                int length = Encoding.ASCII.GetByteCount(message);
                writerDevice.Send(Encoding.ASCII.GetBytes(length.ToString()));
                writerDevice.Send(Encoding.ASCII.GetBytes(message));
            }
            catch (Exception ex)
            {
                Logs.WriteErrorMessage(ex.Message);
            }

            return true;
        }

        /// <summary>
        /// Receive message from another device and encoding of message is Unicode
        /// </summary>
        /// <param name="readerDevice">Socket that use to receive message</param>
        /// <param name="message">Contains that message is received</param>
        /// <returns>
        /// true: success
        /// false: fail
        /// </returns>
        public static bool ReceiveMessageUnicode(Socket readerDevice, out string message)
        {
            message = string.Empty;
            try
            {
                if (readerDevice.Connected == false)
                {
                    return false;
                }

                byte[] bytBuffer = new byte[MAX_SIZE_OF_MESSAGE_LENGTH];
                readerDevice.Receive(bytBuffer);
                message = Encoding.Unicode.GetString(bytBuffer);
            }
            catch (Exception ex)
            {
                Logs.WriteErrorMessage(ex.Message);
                return false;
            }

            return true;
        }

        /// <summary>
        /// Receive message from another device and encoding of message is ASCII
        /// </summary>
        /// <param name="readerDevice">Socket that use to receive message</param>
        /// <param name="message">Message is received</param>
        /// <param name="length">Length of message</param>
        /// <returns>
        /// true: success
        /// false: fail
        /// </returns>
        public static bool ReceiveMessageAscii(Socket readerDevice, out string message, int length)
        {
            message = string.Empty;
            try
            {
                if (readerDevice.Connected == false)
                {
                    return false;
                }

                byte[] bytBuffer = new byte[length];
                readerDevice.Receive(bytBuffer);
                message = Encoding.ASCII.GetString(bytBuffer);
            }
            catch (Exception ex)
            {
                Logs.WriteErrorMessage(ex.Message);
                return false;
            }

            return true;
        }

        /// <summary>
        /// Information about network wifi
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct _SERVER_INFO_100
        {
            /// <summary>
            /// Platform id of network
            /// </summary>
            internal int sv100_platform_id;

            /// <summary>
            /// Host name of PC
            /// </summary>
            [MarshalAs(UnmanagedType.LPWStr)]
            internal string sv100_name;
        }
    }
}